/* -*- mode: c -*-
 * Copyright (C) 2010, 2011 Lluís Vilanova <vilanova@ac.upc.edu>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef TF_CMD_H
#define TF_CMD_H

#include <stdint.h>

#include "tf_common.h"


/** Cpuid command leaf reserved for Teraflux guests. */
#define TF_CPUID 0x80000009


#if TF_GUEST_NATIVE

#ifdef __cplusplus
extern "C" {
#endif
    uint64_t tf_cmd(const tf_cmd_t cmd, const uint64_t * args);
#ifdef __cplusplus
}
#endif

/** Call a command with no arguments. */
#define TF_CMD_0(c)                             \
    ({                                          \
        uint64_t _res = tf_cmd(c, 0);           \
        _res;                                   \
    })


/** Call a command with one argument. */
#define TF_CMD_1(c, a1)                         \
    ({                                          \
        uint64_t _args[1] = {(uint64_t)a1};     \
        uint64_t _res = tf_cmd(c, _args);       \
        _res;                                   \
    })

/** Call a command with two arguments. */
#define TF_CMD_2(c, a1, a2)                     \
    ({                                          \
        uint64_t _args[2] = {(uint64_t)a1,      \
                             (uint64_t)a2};     \
        uint64_t _res = tf_cmd(c, _args);       \
        _res;                                   \
    })

/** Call a command with three arguments. */
#define TF_CMD_3(c, a1, a2, a3)                 \
    ({                                          \
        uint64_t _args[3] = {(uint64_t)a1,      \
                             (uint64_t)a2,      \
                             (uint64_t)a3};     \
        uint64_t _res = tf_cmd(c, _args);       \
        _res;                                   \
    })

#else  /* TF_GUEST_NATIVE */

/* Implemented as macros to avoid any added overhead due to function calls. */

/** Call a command with no arguments. */
#define TF_CMD_0(c)                                     \
    ({                                                  \
        uint64_t _res;                                  \
        __asm__ __volatile__ ("cpuid"                           \
                      :                                 \
                      : SREG(1)(TF_CPUID),              \
                        SREG(2)(c),                     \
                        SREG(3)(&_res),                 \
                        SREG(4)(0),                     \
                        SREG(5)(0),                     \
                        SREG(6)(0)                      \
                      :"memory"                         \
            );                                          \
        _res;                                           \
    })

/** Call a command with one argument. */
#define TF_CMD_1(c, a1)                                 \
    ({                                                  \
        uint64_t _res;                                  \
        __asm__ __volatile__ ("cpuid"                           \
                      :                                 \
                      : SREG(1)(TF_CPUID),              \
                        SREG(2)(c),                     \
                        SREG(3)(a1),                    \
                        SREG(4)(&_res),                 \
                        SREG(5)(0),                     \
                        SREG(6)(0)                      \
                      :"memory"                         \
            );                                          \
        _res;                                           \
    })

/** Call a command with two arguments. */
#define TF_CMD_2(c, a1, a2)                             \
    ({                                                  \
        uint64_t _res;                                  \
        __asm__ __volatile__ ("cpuid"                           \
                      :                                 \
                      : SREG(1)(TF_CPUID),              \
                        SREG(2)(c),                     \
                        SREG(3)(a1),                    \
                        SREG(4)(a2),                    \
                        SREG(5)(&_res),                 \
                        SREG(6)(0)                      \
                      :"memory"                         \
            );                                          \
        _res;                                           \
    })

/** Call a command with three arguments. */
#define TF_CMD_3(c, a1, a2, a3)                         \
    ({                                                  \
        uint64_t _res;                                  \
        __asm__ __volatile__ ("cpuid"                           \
                      :                                 \
                      : SREG(1)(TF_CPUID),              \
                        SREG(2)(c),                     \
                        SREG(3)(a1),                    \
                        SREG(4)(a2),                    \
                        SREG(5)(a3),                    \
                        SREG(6)(&_res)                  \
                      :"memory"                         \
            );                                          \
        _res;                                           \
    })


#define SREG(n) SREG ##n
#define NREG(n) NREG ##n

#define SREG1 "a"
#define NREG1 15
#define SREG2 "D"
#define NREG2  8
#define SREG3 "S"
#define NREG3  9
#define SREG4 "b"
#define NREG4 12
#define SREG5 "c"
#define NREG5 14
#define SREG6 "d"
#define NREG6 13

#endif  /* TF_GUEST_NATIVE */

#endif  /* TF_CMD_H */
